﻿using System;
using System.Linq;
using System.Text;

using Ewk.Math.ComputationTypes;

namespace Ewk.Math.Algebra
{
    /// <summary>
    /// Represents a vector with elements of <see cref="Type"/> <see cref="Computable{T}"/>.
    /// </summary>
    /// <typeparam name="T">The <see cref="Type"/> of the internal value of the elements.</typeparam>
    public class Vector<T> : IEquatable<Vector<T>>
        where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
    {
        private Computable<T>[] _elements;
        private Computable<T> _norm;

        #region Constructors
        /// <summary>
        /// Initializes a new instance with length elements.
        /// </summary>
        /// <param name="length">The number of elements.</param>
        public Vector(int length)
            : this(new Computable<T>[length])
        {
        }

        /// <summary>
        /// Initializes a new instance with the specified internal values.
        /// </summary>
        /// <param name="values">The internal values of the elements of the <see cref="Vector{T}"/>.</param>
        public Vector(params T[] values)
        {
            _elements = values.Select(Computable<T>.Factory.Create).ToArray();
        }

        /// <summary>
        /// Initializes a new instance with the specified elements.
        /// </summary>
        /// <param name="elements">The elements of the <see cref="Vector{T}"/>.</param>
        public Vector(params Computable<T>[] elements)
        {
            _elements = elements;
        }
        #endregion

        /// <summary>
        /// Gets the elements of the vector.
        /// </summary>
        private Computable<T>[] Elements
        {
            get { return _elements; }
            set
            {
                _norm = null;
                _elements = value;
            }
        }

        /// <summary>
        /// Gets or sets the element at the specified index.
        /// </summary>
        public Computable<T> this[int i]
        {
            get { return Elements[i]; }
            set
            {
                _norm = null;
                Elements[i] = value;
            }
        }

        /// <summary>
        /// Gets the dimension of the vector.
        /// </summary>
        public int Length
        {
            get { return Elements.Length; }
        }

        #region Computing
        /// <summary>
        /// Adds the provided <paramref name="value"/> to each element of this vector
        /// </summary>
        /// <param name="value">The value to add.</param>
        public Vector<T> Add(Computable<T> value)
        {
            var result = Add(this, value);
            CopyState(result);

            return this;
        }

        /// <summary>
        /// Adds the provided <paramref name="vector"/> to this vector
        /// </summary>
        /// <param name="vector">The vector to add.</param>
        public Vector<T> Add(Vector<T> vector)
        {
            var result = Add(this, vector);
            CopyState(result);

            return this;
        }

        /// <summary>
        /// Subtracts the provided <paramref name="value"/> from each element of this vector
        /// </summary>
        /// <param name="value">The value to subtract.</param>
        public Vector<T> Subtract(Computable<T> value)
        {
            var result = Subtract(this, value);
            CopyState(result);

            return this;
        }

        /// <summary>
        /// Subtracts the provided <paramref name="vector"/> from this vector
        /// </summary>
        /// <param name="vector">The vector to subtract.</param>
        public Vector<T> Subtract(Vector<T> vector)
        {
            var result = Subtract(this, vector);
            CopyState(result);

            return this;
        }

        /// <summary>
        /// Multiplies the provided <paramref name="value"/> with each element of this vector
        /// </summary>
        /// <param name="value">The value to multiply with.</param>
        public Vector<T> Multiply(Computable<T> value)
        {
            var result = Multiply(this, value);
            CopyState(result);

            return this;
        }

        /// <summary>
        /// Normalizes this <see cref="Vector{T}"/> so that it becomes a unit <see cref="Vector{T}"/>.
        /// </summary>
        public Vector<T> Normalize()
        {
            var result = Unit(this);
            CopyState(result);

            return this;
        }

        /// <summary>
        /// Computes the Euclidean norm of this vector which is also known as 2-Norm.
        /// </summary>
        public Computable<T> Norm
        {
            get { return _norm ?? (_norm = DotProduct(this, this).Sqrt()); }
        }

        #endregion

        #region Equality
        /// <summary>
        /// Returns a value indicating whether this instance and the specified <see cref="Vector{T}"/> represent the same value.
        /// </summary>
        /// <param name="other">The <see cref="Vector{T}"/> to compare with.</param>
        /// <returns>A value indicating whether this instance and the specified <see cref="Vector{T}"/> represent the same value.</returns>
        public bool Equals(Vector<T> other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;

            for (var i = 0; i < Length; i++)
            {
                if (!this[i].Equals(other[i]))
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Returns a value indicating whether this instance and the specified <see cref="object"/> represent the same value.
        /// </summary>
        /// <param name="obj">The <see cref="object"/> to compare with.</param>
        /// <returns>A value indicating whether this instance and the specified <see cref="object"/> represent the same value.</returns>
        public override bool Equals(object obj)
        {
            return Equals(obj as Vector<T>);
        }

        /// <summary>
        /// Serves as a hash function.
        /// </summary>
        /// <returns>An <see cref="int"/> that represents the hash of this instance.</returns>
        public override int GetHashCode()
        {
            return (Elements != null ? Elements.GetHashCode() : 0);
        }
        #endregion

        /// <summary>
        /// Creates a <see cref="string"/> representation  of the <see cref="Vector{T}"/> instance.
        /// </summary>
        /// <returns>A <see cref="string"/> representation  of the <see cref="Vector{T}"/> instance.</returns>
        public override string ToString()
        {
            var sb = new StringBuilder();

            Iterate(Elements.Length, i =>
                                         {
                                             sb.Append(Elements[i]);
                                             sb.Append("\t");
                                         });

            return sb.ToString();
        }

        private void CopyState(Vector<T> other)
        {
            Elements = other.Elements;
            _norm = other._norm;
        }

        #region Static members
        #region Computing
        /// <summary>
        /// Adds a <see cref="Computable{T}"/> instance to every element
        /// of the specified <see cref="Vector{T}"/> instance.
        /// </summary>
        public static Vector<T> Add(Vector<T> vector, Computable<T> value)
        {
            if (vector == null) return null;
            if (value == null)
            {
                value = Computable<T>.Factory.Zero;
            }

            var result = new Vector<T>(vector.Length);

            Iterate(vector.Length, i => result[i] = vector[i] + value);

            return result;
        }

        /// <summary>
        /// Adds two <see cref="Vector{T}"/> instances.
        /// </summary>
        public static Vector<T> Add(Vector<T> a, Vector<T> b)
        {
            if (a == null || b == null)
            {
                if (a == null && b == null)
                {
                    return null;
                }

                if (a == null)
                {
                    a = Zero(b.Length);
                }
                else
                {
                    b = Zero(a.Length);
                }
            }

            var result = new Vector<T>(a.Length);

            Iterate(a, b, i => result[i] = a[i] + b[i]);

            return result;
        }

        /// <summary>
        /// Subtracts a <see cref="Computable{T}"/> instance from every element
        /// of the specified <see cref="Vector{T}"/> instance.
        /// </summary>
        public static Vector<T> Subtract(Vector<T> vector, Computable<T> value)
        {
            if (vector == null) return null;
            if (value == null)
            {
                value = Computable<T>.Factory.Zero;
            }

            var result = new Vector<T>(vector.Length);

            Iterate(vector.Length, i => result[i] = vector[i] - value);

            return result;
        }

        /// <summary>
        /// Subtracts <see cref="Vector{T}">b</see> from <see cref="Vector{T}">a</see>.
        /// </summary>
        public static Vector<T> Subtract(Vector<T> a, Vector<T> b)
        {
            if (a == null || b == null)
            {
                if (a == null && b == null)
                {
                    return null;
                }

                if (a == null)
                {
                    a = Zero(b.Length);
                }
                else
                {
                    b = Zero(a.Length);
                }
            }

            var result = new Vector<T>(a.Length);

            Iterate(a, b, i => result[i] = a[i] - b[i]);

            return result;
        }

        /// <summary>
        /// Transforms the specified <see cref="Vector{T}"/> by multiplying it 
        /// with the specified <see cref="Matrix{T}"/>.
        /// </summary>
        /// <param name="matrix">The <see cref="Matrix{T}"/> that represents a transformation.</param>
        /// <param name="vector">The <see cref="Vector{T}"/> that has to be transformed.</param>
        /// <returns>A transformed <see cref="Vector{T}"/>.</returns>
        public static Vector<T> Multiply(Matrix<T> matrix, Vector<T> vector)
        {
            if (matrix == null || vector == null)
            {
                if (matrix == null && vector == null)
                {
                    return null;
                }

                return matrix == null
                           ? Zero(vector.Length)
                           : Zero(matrix.RowCount);
            }

            var result = new Vector<T>(matrix.RowCount);

            Iterate(matrix.RowCount, i => result[i] = matrix.GetRow(i) * vector);

            return result;
        }

        /// <summary>
        /// Multiplies a <see cref="Computable{T}"/> instance with every element
        /// of the specified <see cref="Vector{T}"/> instance.
        /// </summary>
        public static Vector<T> Multiply(Vector<T> vector, Computable<T> value)
        {
            if (vector == null) return null;
            if (value == null)
            {
                return Zero(vector.Length);
            }

            var result = new Vector<T>(vector.Length);

            Iterate(vector.Length, i => result[i] = vector[i] *value);

            return result;
        }

        /// <summary>
        /// Divides every element of the specified <see cref="Vector{T}"/> instance
        /// by the specified <see cref="Computable{T}"/> instance.
        /// </summary>
        public static Vector<T> Divide(Vector<T> vector, Computable<T> value)
        {
            if (vector == null) return null;
            if (value == null) return null;

            var result = new Vector<T>(vector.Length);

            Iterate(vector.Length, i => result[i] = vector[i] / value);

            return result;
        }

        /// <summary>
        /// Computes the Unit <see cref="Vector{T}"/> for the specified <see cref="Vector{T}"/>.
        /// </summary>
        /// <param name="vector">The <see cref="Vector{T}"/> to use for the computation.</param>
        /// <returns>
        /// A <see cref="Vector{T}"/> that represents the Unit <see cref="Vector{T}"/>
        /// for the specified <see cref="Vector{T}"/>.
        /// </returns>
        public static Vector<T> Unit(Vector<T> vector)
        {
            return vector == null ||
                   vector == Zero(vector.Length)
                       ? null
                       : vector/vector.Norm;
        }

        /// <summary>
        /// Computes the dot product of the specified <see cref="Vector{T}"/> instances.
        /// </summary>
        /// <returns>
        /// A <see cref="Computable{T}"/> that represents the 
        /// dot product of the <see cref="Vector{T}"/> instances.
        /// </returns>
        public static Computable<T> DotProduct(Vector<T> a, Vector<T> b)
        {
            if (a == null || b == null)
            {
                if (a == null && b == null)
                {
                    return null;
                }

                return Computable<T>.Factory.Zero;
            }

            var dotProduct = Computable<T>.Factory.Zero;

            Iterate(a, b, i => dotProduct += a[i] * b[i]);

            return dotProduct;
        }

        /// <summary>
        /// Computes the cosine of the angle between the specified
        /// <see cref="Vector{T}"/> instances.
        /// </summary>
        /// <returns>
        /// A <see cref="Computable{T}"/> that represents the 
        /// cosine of the angle between the <see cref="Vector{T}"/> instances.
        /// </returns>
        public static Computable<T> CosAngle(Vector<T> a, Vector<T> b)
        {
            var dotProduct = DotProduct(a, b);

            return dotProduct / (a.Norm * b.Norm);
        }

        /// <summary>
        /// Computes the cross product of the specified <see cref="Vector{T}"/> instances.
        /// </summary>
        /// <returns>
        /// A <see cref="Computable{T}"/> that represents the 
        /// cross product of the <see cref="Vector{T}"/> instances.
        /// </returns>
        public static Vector<T> CrossProduct(Vector<T> a, Vector<T> b)
        {
            if (a == null || b == null)
            {
                if (a == null && b == null)
                {
                    return null;
                }

                return Zero(a == null ? b.Length : a.Length);
            }

            if (a.Length != 3 || b.Length != 3)
            {
                throw new InvalidOperationException("The cross product can only be done on vector in 3D.");
            }

            var result = new Vector<T>(3);
            result[0] = a[1] * b[2] - a[2] * b[1];
            result[1] = a[2] * b[0] - a[0] * b[2];
            result[2] = a[0] * b[1] - a[1] * b[0];

            return result;
        }

        /// <summary>
        /// Computes the Euclidean norm of the difference <see cref="Vector{T}"/>.
        /// of two <see cref="Vector{T}"/>s.
        /// </summary>
        /// <param name="a">The first <see cref="Vector{T}"/>.</param>
        /// <param name="b">The second <see cref="Vector{T}"/>.</param>
        /// <returns>
        /// A <see cref="Computable{T}"/> that represents the Euclidean
        /// norm of the distance between two <see cref="Vector{T}"/>s.
        /// </returns>
        public static Computable<T> Distance(Vector<T> a, Vector<T> b)
        {
            if (a == null || b == null)
            {
                if (a == null && b == null)
                {
                    return null;
                }

                return a == null ? b.Norm : a.Norm;
            }

            return (a - b).Norm;
        }

        #endregion

        /// <summary>
        /// Represents the zero <see cref="Vector{T}"/>.
        /// </summary>
        /// <param name="length">The dimension of the <see cref="Vector{T}"/>.</param>
        /// <returns>The zero <see cref="Vector{T}"/>.</returns>
        public static Vector<T> Zero(int length)
        {
            var elements = new Computable<T>[length];

            Iterate(length, i => elements[i] = Computable<T>.Factory.Zero);

            return new Vector<T>(elements);
        }

        #region Overloaded operators
        /// <summary>
        /// Adds two <see cref="Vector{T}"/> instances.
        /// </summary>
        public static Vector<T> operator +(Vector<T> a, Vector<T> b)
        {
            return Add(a, b);
        }

        /// <summary>
        /// Adds a <see cref="Computable{T}"/> instance to every element
        /// of the specified <see cref="Vector{T}"/> instance.
        /// </summary>
        public static Vector<T> operator +(Vector<T> a, Computable<T> b)
        {
            return Add(a, b);
        }

        /// <summary>
        /// Subtracts <see cref="Vector{T}">b</see> from <see cref="Vector{T}">a</see>.
        /// </summary>
        public static Vector<T> operator -(Vector<T> a, Vector<T> b)
        {
            return Subtract(a, b);
        }

        /// <summary>
        /// Subtracts a <see cref="Computable{T}"/> instance from every element
        /// of the specified <see cref="Vector{T}"/> instance.
        /// </summary>
        public static Vector<T> operator -(Vector<T> a, Computable<T> b)
        {
            return Subtract(a, b);
        }

        /// <summary>
        /// Transforms the specified <see cref="Vector{T}"/> by multiplying it 
        /// with the specified <see cref="Matrix{T}"/>.
        /// </summary>
        /// <param name="matrix">The <see cref="Matrix{T}"/> that represents a transformation.</param>
        /// <param name="vector">The <see cref="Vector{T}"/> that has to be transformed.</param>
        /// <returns>A transformed <see cref="Vector{T}"/>.</returns>
        public static Vector<T> operator *(Matrix<T> matrix, Vector<T> vector)
        {
            return Multiply(matrix, vector);
        }

        /// <summary>
        /// Computes the dot product of the specified <see cref="Vector{T}"/> instances.
        /// </summary>
        /// <returns>
        /// A <see cref="Computable{T}"/> that represents the 
        /// dot product of the <see cref="Vector{T}"/> instances.
        /// </returns>
        public static Computable<T> operator *(Vector<T> a, Vector<T> b)
        {
            return DotProduct(a, b);
        }

        /// <summary>
        /// Multiplies a <see cref="Computable{T}"/> instance with every element
        /// of the specified <see cref="Vector{T}"/> instance.
        /// </summary>
        public static Vector<T> operator *(Vector<T> a, Computable<T> b)
        {
            return Multiply(a, b);
        }

        /// <summary>
        /// Divides every element of the specified <see cref="Vector{T}"/> instance
        /// by the specified <see cref="Computable{T}"/> instance.
        /// </summary>
        public static Vector<T> operator /(Vector<T> a, Computable<T> b)
        {
            return Divide(a, b);
        }

        /// <summary>
        /// Returns a value that indicates if <see cref="Vector{T}">a</see> is equal to <see cref="Vector{T}">b</see>.
        /// </summary>
        public static bool operator ==(Vector<T> a, Vector<T> b)
        {
            if (ReferenceEquals(null, a))
            {
                return ReferenceEquals(null, b) || b.Equals(null);
            }

            return a.Equals(b);
        }

        /// <summary>
        /// Returns a value that indicates if <see cref="Vector{T}">a</see> is not equal to <see cref="Vector{T}">b</see>.
        /// </summary>
        public static bool operator !=(Vector<T> a, Vector<T> b)
        {
            return !(a == b);
        }
        #endregion

        internal static void Iterate(int length, Action<int> action)
        {
            for (var i = 0; i < length; i++)
            {
                action(i);
            }
        }

        internal static void Iterate(Vector<T> a, Vector<T> b, Action<int> action)
        {
            if (a.Length != b.Length)
            {
                throw new InvalidOperationException("Dimensions are not equal.");
            }

            Iterate(a.Length, action);
        }
        #endregion
    }
}
